/*
 * @Author: wuqingchun
 * @Date: 2023-08-20 16:28:16
 * @Description: 
 * @LastEditors: wuqingchun
 * @LastEditTime: 2024-01-08 15:49:48
 */
/* PI - protocol interpreter 协议解析器 */
#include <stdio.h>

#include "ftpc_pi.h"

// 使用状态机解析
static int __pi_StateMachine(FTPC_PI_CONTEXT_S *pstCtx, const char *pcData, size_t ulDataLen)
{
    int iErr = FTPC_PI_AGAIN;
    char *pcStart = (char *)pcData;
    char *pcEnd = pcStart + ulDataLen;
    // 开始解析
    // 应答状态码第一个数字
    // 应答状态码第二个数字

    FTPC_PI_STATE_E enState = pstCtx->enState;

    while (pcStart < pcEnd)
    {
        switch (enState)
        {
            case FTPC_PI_STATE_INIT:
            {
                if (*pcStart > '5' || *pcStart < '1')
                {
                    // 非法包，直接丢弃
                    iErr = FTPC_PI_FAILED;
                    break; 
                }

                pstCtx->szCode[0] = *pcStart;

                pstCtx->iReplyCode = *pcStart - '0';

                enState = FTPC_PI_STATE_CODE_1;
                break;
            }
            case FTPC_PI_STATE_CODE_1:
            {
                if (*pcStart > '9' || *pcStart < '0')
                {
                    // 非法包，直接丢弃
                    iErr = FTPC_PI_FAILED;
                    break; 
                }

                pstCtx->szCode[1] = *pcStart;

                pstCtx->iReplyCode = pstCtx->iReplyCode * 10 + *pcStart - '0';
                enState = FTPC_PI_STATE_CODE_2;
                break;
            }
            case FTPC_PI_STATE_CODE_2:
            {
                if (*pcStart > '9' || *pcStart < '0')
                {
                    // 非法包，直接丢弃
                    iErr = FTPC_PI_FAILED;
                    break; 
                }

                pstCtx->szCode[2] = *pcStart;
                pstCtx->iReplyCode = pstCtx->iReplyCode * 10 + *pcStart - '0';
                enState = FTPC_PI_STATE_CODE_3;
                break;
            }
            case FTPC_PI_STATE_CODE_3:
            {
                pstCtx->szCode[3] = '\0';

                if (pstCtx->iReplyCode == 227)
                {
                    enState = FTPC_PI_ADDR_PORT_BEGIN;  
                }
                else if (*pcStart == ' ')
                {
                    enState = FTPC_PI_STATE_SLINE_TEXT;
                }
                else if (*pcStart == '-')
                {
                    enState = FTPC_PI_STATE_MLINE_TEXT;
                }
                else
                {
                    // 非法包，直接丢弃
                    iErr = FTPC_PI_FAILED;
                    break;
                }
                
                break;
            }
            case FTPC_PI_ADDR_PORT_BEGIN:
            {
                if (*pcStart == '(')
                {
                    enState = FTPC_PI_ADDR_PORT_END;
                }

                break;
            }
            case FTPC_PI_ADDR_PORT_END:
            {
                if (*pcStart == ')')
                {
                    enState = FTPC_PI_STATE_SLINE_TEXT;
                    pstCtx->szAddrPort[pstCtx->uiAddrPortLen++] = '\0';
                }
                else
                {
                    pstCtx->szAddrPort[pstCtx->uiAddrPortLen++] = *pcStart;
                }

                break;
            }
            case FTPC_PI_STATE_SLINE_TEXT:
            {
                if (*pcStart == '\r')
                {
                    enState = FTPC_PI_STATE_END_CR;
                }

                break;
            }
            case FTPC_PI_STATE_MLINE_TEXT:
            {
                if (*pcStart == '\r')
                {
                    enState = FTPC_PI_STATE_MLINE_CR;
                }
                break;
            }
            case FTPC_PI_STATE_MLINE_CR:
            {
                if (*pcStart == '\n')
                {
                    enState = FTPC_PI_STATE_MLINE_LF;
                }

                break;
            }
            case FTPC_PI_STATE_MLINE_LF:
            {
                if (pstCtx->szCode[0] != *pcStart)
                {
                    iErr = FTPC_PI_FAILED;
                    break;
                }

                enState = FTPC_PI_STATE_MLINE_CODE_1;
                break;
            } 
            case FTPC_PI_STATE_MLINE_CODE_1:
            {
                if (pstCtx->szCode[1] != *pcStart)
                {
                    iErr = FTPC_PI_FAILED;
                    break;
                }

                enState = FTPC_PI_STATE_MLINE_CODE_2;
                break;
            }
            case FTPC_PI_STATE_MLINE_CODE_2:
            {
                if (pstCtx->szCode[2] != *pcStart)
                {
                    iErr = FTPC_PI_FAILED;
                    break;
                }

                enState = FTPC_PI_STATE_MLINE_CODE_3;
                break;
            }
            case FTPC_PI_STATE_MLINE_CODE_3:
            {
                if (*pcStart == ' ')
                {
                    enState = FTPC_PI_STATE_SLINE_TEXT;
                }
                else if (*pcStart == '-')
                {
                    enState = FTPC_PI_STATE_MLINE_TEXT;
                }
                else
                {
                    iErr = FTPC_PI_FAILED;
                    break;
                }

                break;
            }
            case FTPC_PI_STATE_END_CR:
            {
                if (*pcStart == '\n')
                {
                    enState = FTPC_PI_STATE_END;
                    iErr = FTPC_PI_OK;
                }
                else
                {
                    enState = FTPC_PI_STATE_SLINE_TEXT;
                }

                break;
            }
        }

        if (iErr != FTPC_PI_AGAIN)
        {
            break;
        }

        pcStart++;
    }

    if (iErr == FTPC_PI_FAILED)
    {
        pstCtx->enState = FTPC_PI_STATE_ERROR;
    }
    else
    {
        pstCtx->enState = enState;
    }

    return iErr;
}

void FTPC_pi_Init(FTPC_PI_CONTEXT_S *pstCtx)
{
    pstCtx->enState = FTPC_PI_STATE_INIT;
    pstCtx->iReplyCode = 0;
    pstCtx->uiAddrPortLen = 0;
}

int FTPC_pi_ParseReply(FTPC_PI_CONTEXT_S *pstCtx, const char *pcData, size_t ulDataLen)
{
    return __pi_StateMachine(pstCtx, pcData, ulDataLen);
}

